import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting';
import projectSetting from '@/settings/projectSetting';
import { useAppStoreOutside } from '@/store/modules/app';
import { useUserStoreOutside } from '@/store/modules/user';
import { AxiosCanceler } from '@/utils/axios/axiosCancel';
import { Modal, notification } from 'ant-design-vue';
import { unref } from 'vue';
import type { RouteLocationNormalized, Router } from 'vue-router';
import nProgress from 'nprogress';
import { setRouteChange } from '../helper/routeChangeHelper';
import { createPermissionGuard } from './permissionGuard';
import { createParamMenuGuard } from './paramMenuGuard';
import { createStateGuard } from './stateGuard';

export function setupRouterGuard(router: Router) {
    createPageGuard(router);
    createPageLoadingGuard(router);
    createHttpGuard(router);
    createScrollGuard(router);
    createMessageGuard(router);
    createProgressGuard(router);
    createPermissionGuard(router);
    createParamMenuGuard(router); // must after createPermissionGuard (menu has been built.)
    createStateGuard(router);
}

function createPageGuard(router: Router) {
	const loadedPageMap = new Map<string, boolean>();

	router.beforeEach(async to => {
		to.meta.loaded = !!loadedPageMap.get(to.path);
		setRouteChange(to);

		return true;
	});

	router.afterEach(to => {
		loadedPageMap.set(to.path, true);
	});
}

function createPageLoadingGuard(router: Router) {
	const userStore = useUserStoreOutside();
	const appStore = useAppStoreOutside();
	const { getOpenPageLoading } = useTransitionSetting();
	router.beforeEach(async to => {
		if (!userStore.getToken) {
			return true;
		}
		if (to.meta.loaded) {
			return true;
		}

		if (unref(getOpenPageLoading)) {
			appStore.setPageLoadingAction(true);
			return true;
		}

		return true;
	});
	router.afterEach(async () => {
		if (unref(getOpenPageLoading)) {
			// TODO Looking for a better way
			// The timer simulates the loading time to prevent flashing too fast,
			setTimeout(() => {
				appStore.setPageLoading(false);
			}, 220);
		}
		return true;
	});
}

function createHttpGuard(router: Router) {
	const { removeAllHttpPending } = projectSetting;
	let axiosCanceler: Nullable<AxiosCanceler>;
	if (removeAllHttpPending) {
		axiosCanceler = new AxiosCanceler();
	}
	router.beforeEach(async () => {
		// Switching the route will delete the previous request
		axiosCanceler?.removeAllPending();
		return true;
	});
}

function createScrollGuard(router: Router) {
	const isHash = (href: string) => {
		return /^#/.test(href);
	};

	const body = document.body;

	router.afterEach(async to => {
		// scroll top
		isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0);
		return true;
	});
}

export function createMessageGuard(router: Router) {
	const { closeMessageOnSwitch } = projectSetting;

	router.beforeEach(async () => {
		try {
			if (closeMessageOnSwitch) {
				Modal.destroyAll();
				notification.destroy();
			}
		} catch (error) {
			console.warn('message guard error:' + error);
		}
		return true;
	});
}

export function createProgressGuard(router: Router) {
	const { getOpenNProgress } = useTransitionSetting();
	router.beforeEach(async to => {
		if (to.meta.loaded) {
			return true;
		}
		unref(getOpenNProgress) && nProgress.start();
		return true;
	});

	router.afterEach(async () => {
		unref(getOpenNProgress) && nProgress.done();
		return true;
	});
}
